﻿using System;
using System.Linq;
using System.Windows.Input;
using FluentValidation;
using LINQPad.Extensibility.DataContext;
using LINQPad.OpenAccess.Helpers;
using LINQPad.OpenAccess.Settings;
using LINQPad.OpenAccess.UI.Commands;
using LINQPad.OpenAccess.UI.Events;
using LINQPad.OpenAccess.UI.Services;

namespace LINQPad.OpenAccess.UI.ViewModel
{
    /// <summary>
    /// DriverSettings - view model.
    /// </summary>
    public class DriverSettingsViewModel : ValidatableViewModelBase<DriverSettingsViewModel>, IPersistableSettingsViewModel<DriverSettings>
    {
        #region Private

        #region - Vars

        private DelegateCommand<object> __saveCommand, __cancelCommand;
        private BackendSettingsViewModel _backendSettings;
        private BasicSettingsViewModel _basicSettings;
        private ConnectionSettingsHostViewModel _connectionSettingsHost;
        private IUserInteractionService _userInteractionService;

        #endregion

        #region - Command Handlers

        /// <summary>
        /// Handles the 'CanExecute' event of the cancel command.
        /// </summary>
        /// <param name="parameter">The parameter.</param>
        private bool Cancel_CanExecute( object parameter )
        {
            return true;
        }
        /// <summary>
        /// Handles the 'Execute' event of the cancel command.
        /// </summary>
        /// <param name="parameter">The parameter.</param>
        private void Cancel_Execute( object parameter )
        {
            this.OnCloseViewRequested( dueCancelation: true );
        }
        /// <summary>
        /// Handles the 'CanExecute' event of the save command.
        /// </summary>
        /// <param name="parameter">The parameter.</param>
        private bool Save_CanExecute( object parameter )
        {
            return this.IsValid;
        }
        /// <summary>
        /// Handles the 'Execute' event of the save command.
        /// </summary>
        /// <param name="parameter">The parameter.</param>
        private void Save_Execute( object parameter )
        {
            var result = this.ValidateInstance( );
            if ( result != null && !result.IsValid )
            {
                this._userInteractionService.ShowCannotSaveDueToValidationErrorsNotification( result.Errors );
                return;
            }
            this.OnCloseViewRequested( dueCancelation: false );
        }

        #endregion

        #endregion

        #region Protected

        #region - Functions

        /// <summary>
        /// Raises the <see cref="E:CloseViewRequested" /> event.
        /// </summary>
        /// <param name="dueCancelation">if set to <c>true</c> if the view should be closed due to cancelation.</param>
        protected virtual void OnCloseViewRequested( bool dueCancelation )
        {
            var handler = this.CloseViewRequested;
            if ( handler != null )
                handler( this, new CloseViewRequestedEventArgs( !dueCancelation ) );
        }
        /// <summary>
        /// Raises the 'CanExecuteChanged' event for the view model's commands.
        /// </summary>
        protected virtual void RaiseCanExecuteChangedForCommands( )
        {
            if ( this.__saveCommand != null )
                this.__saveCommand.RaiseCanExecuteChanged( );
            if ( this.__cancelCommand != null )
                this.__cancelCommand.RaiseCanExecuteChanged( );
        }

        #endregion

        #endregion

        #region Public

        #region - Constructors

        /// <summary>
        /// Initializes a new instance of the <see cref="DriverSettingsViewModel" /> class.
        /// </summary>
        /// <param name="basicSettings">The basic settings.</param>
        /// <param name="backendSettings">The backend settings.</param>
        /// <param name="validator">The validator.</param>
        public DriverSettingsViewModel( IUserInteractionService userInteractionService
                                        , BasicSettingsViewModel basicSettings, BackendSettingsViewModel backendSettings
                                        , ConnectionSettingsHostViewModel connectionSettingsHost
                                        , IValidator<DriverSettingsViewModel> validator )
            : base( validator )
        {
            Guard.ArgumentNotNull( userInteractionService, "userInteractionService" );
            Guard.ArgumentNotNull( basicSettings, "basicSettings" );
            Guard.ArgumentNotNull( backendSettings, "backendSettings" );
            Guard.ArgumentNotNull( connectionSettingsHost, "connectionSettingsHost" );
            Guard.ArgumentNotNull( validator, "validator" );

            this._userInteractionService = userInteractionService;
            this._backendSettings = backendSettings;
            this._basicSettings = basicSettings;
            this._connectionSettingsHost = connectionSettingsHost;

            this.RaisePropertyChangedValidationDependentProperties = true;

            backendSettings.Owner = this;
            basicSettings.Owner = this;
            connectionSettingsHost.Owner = this;
        }

        #endregion

        #region - Functions

        /// <summary>
        /// Refreshes this instance.
        /// </summary>
        public void Refresh( )
        {
            this._basicSettings.Refresh( );
        }

        #endregion

        #region - Properties

        /// <summary>
        /// Gets the backend settings.
        /// </summary>
        /// <value>
        /// The backend settings.
        /// </value>
        public BackendSettingsViewModel BackendSettings
        {
            get
            {
                return this._backendSettings;
            }
        }
        /// <summary>
        /// Gets the basic settings.
        /// </summary>
        /// <value>
        /// The basic settings.
        /// </value>
        public BasicSettingsViewModel BasicSettings
        {
            get
            {
                return this._basicSettings;
            }
        }
        /// <summary>
        /// Gets the connection settings host.
        /// </summary>
        /// <value>
        /// The connection settings host.
        /// </value>
        public ConnectionSettingsHostViewModel ConnectionSettingsHost
        {
            get
            {
                return this._connectionSettingsHost;
            }
        }

        #endregion

        #region - Commands

        /// <summary>
        /// Gets the cancel command.
        /// </summary>
        /// <value>
        /// The cancel command.
        /// </value>
        public ICommand CancelCommand
        {
            get
            {
                this.ThrowIfDisposed( );
                return this.__cancelCommand ?? ( this.__cancelCommand = new DelegateCommand<object>( Cancel_Execute, Cancel_CanExecute ) );
            }
        }
        /// <summary>
        /// Gets the save command.
        /// </summary>
        /// <value>
        /// The save command.
        /// </value>
        public ICommand SaveCommand
        {
            get
            {
                this.ThrowIfDisposed( );
                return this.__saveCommand ?? ( this.__saveCommand = new DelegateCommand<object>( Save_Execute, Save_CanExecute ) );
            }
        }

        #endregion

        #region - Events

        /// <summary>
        /// Occurs when the user requests to close the view.
        /// </summary>
        public event EventHandler<CloseViewRequestedEventArgs> CloseViewRequested;

        #endregion

        #endregion

        #region ValidatableViewModelBase<DriverSettingsViewModel> Members

        /// <summary>
        /// Releases unmanaged and - optionally - managed resources.
        /// </summary>
        /// <param name="disposing"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.</param>
        protected override void Dispose( bool disposing )
        {
            if ( disposing )
            {
                this.DisposeField( ref this.__cancelCommand );
                this.DisposeField( ref this.__saveCommand );
                this.DisposeField( ref this._backendSettings );
                this.DisposeField( ref this._basicSettings );
                this.DisposeField( ref this._connectionSettingsHost );
                this._userInteractionService = null;
            }
            base.Dispose( disposing );
        }
        /// <summary>
        /// Called when a validation dependent property has changed.
        /// </summary>
        protected override void OnValidationDependentPropertyChanged( )
        {
            base.OnValidationDependentPropertyChanged( );
            this.RaiseCanExecuteChangedForCommands( );
        }

        #endregion

        #region IPersistableSettingsViewModel<DriverSettings> Members

        /// <summary>
        /// Tries to load the view model's settings from the specified sources.
        /// </summary>
        /// <param name="connectionInfo">The connection info.</param>
        /// <param name="source">The source.</param>
        /// <returns>
        ///   <c>true</c> on success; otherwise, <c>false</c>.
        /// </returns>
        public bool TryLoadSettingsFrom( IConnectionInfo connectionInfo, DriverSettings source )
        {
            var basicSettingsLoaded = this._basicSettings.TryLoadSettingsFrom( connectionInfo, source );
            var backendSettingsLoaded = this._backendSettings.TryLoadSettingsFrom( connectionInfo, source );
            var connectionSettingsHostLoaded = this._connectionSettingsHost.TryLoadSettingsFrom( connectionInfo, source );

            return basicSettingsLoaded && backendSettingsLoaded && connectionSettingsHostLoaded;
        }
        /// <summary>
        /// Writes the view model's settings to the specified targets.
        /// </summary>
        /// <param name="connectionInfo">The connection info.</param>
        /// <param name="target">The target.</param>
        public void WriteSettingsTo( IConnectionInfo connectionInfo, DriverSettings target )
        {
            Guard.ArgumentNotNull( connectionInfo, "connectionInfo" );
            Guard.ArgumentNotNull( target, "target" );

            var result = this.ValidateInstance( );
            if ( result != null && !result.IsValid )
                throw new ValidationException( result.Errors );

            this._basicSettings.WriteSettingsTo( connectionInfo, target );
            this._backendSettings.WriteSettingsTo( connectionInfo, target );
            this._connectionSettingsHost.WriteSettingsTo( connectionInfo, target );
        }

        #endregion
    }
}
